/*******************************************************************************
 * Copyright (c) 2010, 2018 西安秦晔信息科技有限公司
 * Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *******************************************************************************/
package com.qinyeit.webapp.security;

import com.qinyetech.springstage.core.security.util.HmacSHA256Utils;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * <p>ClassName: com.qinyeit.shirostarterdemo.security.StatelessRequestSignToken
 * <p>Function: 签名数据token
 *  凭证即客户端传入的消息摘要。
 * <p>date: 2018-08-13 18:35
 *
 * @author wuqing
 * @version 1.0
 * @since JDK 1.8
 */
@Slf4j
@SuppressWarnings("unchecked")
public class StatelessRequestSignToken {
    private String username;//用户身份即用户名；
    private Map<String,?> params;//参数.
    private String requestBody;//请求体
    private String timestamp;//时间戳
    private String uri;//请求的uri

    @Setter
    private String securityKey; //安全密钥

    private String clientSign;//凭证即客户端传入的消息摘要。
    private String getContentToSign(){
        //用户名|时间戳|uri|参数值|requestBody
        return username+"|"+timestamp+"|"+uri+"|"+paramsToString()+"|"+Optional.ofNullable(requestBody).orElse("");
    }
    public StatelessRequestSignToken(String username, String timestamp,String uri, String clientSign,Map<String, ?> params) {
        this.username = username;
        this.timestamp=timestamp;
        this.uri=uri;
        this.params = params;
        this.clientSign = clientSign;
    }

    public StatelessRequestSignToken(String username, String timestamp,String uri, String clientSign,Map<String, ?> params,String requestBody) {
        this(username,timestamp,uri,clientSign,params);
        this.requestBody=requestBody;
    }

    private String paramsToString(){
        if(CollectionUtils.isEmpty(params)){
            return "";
        }
        StringBuilder s = new StringBuilder();
        //所有的值按照字典排序
        List<String> paramValues=new ArrayList<>();
        params.values().stream().forEach(item->{
            if(item instanceof String[]) {
                for(String value : (String[])item) {
                    paramValues.add(value);
                }
            } else if(item instanceof List) {
                for(String value : (List<String>)item) {
                    paramValues.add(value);
                }
            } else if(item!=null){
                paramValues.add(((Object) item).toString());
            }
        });
        paramValues.stream().sorted().forEach(s::append);
        return s.toString();
    }

    /**
     * token 基础验证
     * @return
     */
    public boolean baseValidate(){
        if(!StringUtils.hasText(username)){
            return false;
        }
        if(!StringUtils.hasText(timestamp)){
            return false;
        }
        if(!StringUtils.hasText(uri)){
            return false;
        }
        if(!StringUtils.hasText(clientSign)){
            return false;
        }
        return true;
    }
    //签名认证
    public boolean signValidate(){
        String serverDigest = HmacSHA256Utils.digest(securityKey, getContentToSign());
        log.debug("signContent:[{}],signServer:[{}],signClient:[{}],securityKey:[{}]"
                ,getContentToSign()
                ,serverDigest
                ,clientSign
                ,securityKey);
        //进行客户端消息摘要和服务器端消息摘要的匹配
        return serverDigest.equals(clientSign);
    }


}
